function jda_busy_kittens_initialise() {
	window.jda_busy_kittens = {
		speed: {
			paused: false,
			toggle: function () {
				if (this.paused === true) {
					console.log("starting game");
					gamePage.start();
					this.paused = false;
					$('#gamespeedplaystop').prop('value', "STOP");
				} else {
					console.log("pausing game");
					gamePage.worker.terminate();
					gamePage.worker = undefined;
					this.paused = true;
					$('#gamespeedplaystop').prop('value', "PLAY");
				}
			},
			setRate: function (r) {
				if (r < 1) {
					r = 1;
				}
				gamePage.rate = r;
				$('#gamespeedcurrentrate').html(gamePage.rate);
				if (gamePage.worker !== undefined) {
					gamePage.worker.terminate();
					gamePage.start();
				}
			},
			reset: function() {
				this.setRate(5);
			},
			faster: function(r) {
				rate = gamePage.rate + r;
				this.setRate(rate);
			},
			slower: function(r) {
				rate = gamePage.rate - r;
				if (gamePage.rate < 1) { gamePage.rate = 1; }
				this.setRate(rate);
			}
		},
		build: {
			is_limited: function(building) { // check if max resources limit was hit, so no more buildings can be build
				a = building.needed;
				for (x in a) {
					needed = a[x].val;
					resMax = gamePage.resPool.get(a[x].name).maxValue;
					if (resMax === 0) {
						continue;
					}
					if (needed > resMax) {
						return true;
					}
				}
				return false;
			},
			// this checks if building can be built. it also adds resources required to dict 'reserved_res'
			can_be_built: function(name, glob_res, local_res) {
				a = this.buildings[name].needed;
				for (var x in a) {
					var needed = a[x].val;
					var res = gamePage.resPool.get(a[x].name).value;
					if (glob_res[a[x].name] !== undefined) {
						// return false only if after buying this structure (res - needed < glob_res[a[x].name])
						// we'll be left with less resources than required for higher priority structure
						if (res - needed < glob_res[a[x].name]) {
							return false;
						}
					}
					if (local_res[a[x].name] === undefined || local_res[a[x].name] < needed) {
						local_res[a[x].name] = needed;
					}
					if (res < needed) {
						return false;
					}
				}
				return true;
			},
			buildings: {},
			render_pending: false,
			rescan: function() {
				var bd = gamePage.bld.buildingsData;
				for (x in bd) {
					var nm = bd[x].name;
					if (bd[x].unlocked) { // if building is unlocked and not on the buildings list, put it there
						if (this.buildings[nm] === undefined) {
							this.buildings[nm] = {
								autobuilt: 0,
								name: nm,
								label: bd[x].label,
								group: 0,
								count: gamePage.bld.get(nm).val,
								limited: false,
								needed: gamePage.bld.getPrices(nm) };
								this.render_pending = true;
						}
					} else { // if buildings isn't unlocked but for some reason is on the list, remove
						if (this.buildings[nm] !== undefined) {
							delete this.buildings[nm];
							this.render_pending = true;
						}
					}
				}
			},
			// start: function() { this.bldTimer = setInterval(function() { jda_busy_kittens.build.build(); }, 200); },
			// stop: function() { clearInterval(this.bldTimer); this.bldTimer = undefined; },
			global_reserved_resources: { },
			update_reserved_resources: function(reserve) {
				for (y in reserve) {
					if (this.global_reserved_resources[y] === undefined) {
						this.global_reserved_resources[y] = reserve[y];
					} else {
						if (this.global_reserved_resources[y] < reserve[y]) {
							this.global_reserved_resources[y] = reserve[y];
						}
					}
				}
			},
			select_candidate: function() {
				// for (var prop in this.global_reserved_resources) {
					// delete this.global_reserved_resources[prop];
				// }
				this.global_reserved_resources = { };
				// in the beginning, both sets are empty. when single group is scanned, resources required by not capped
				// buildings in this group are added to the local_reserved_resources. when group checking is finished and
				// nothing was selected for build, local_reserved_resources are added to global_reserved_resources and
				// next group is checked. this is repeated for all groups.
				// console.log("Global resources");
				// console.log(this.global_reserved_resources);
				var rv = null;
				for (var x = this.groups.length - 1; x > 0; --x) { // > 0 since group 0 is "no-auto-build"
					var local_reserved_resources = { };
					var g = this.groups[x];
					if (g === undefined || g.list === undefined || g.list.length === 0) {
						continue;
					}
					if (this.is_group_limited(g)) {
						continue;
					}
					// group is not limited (i.e. there is at least one building which is not res-capped)
					for (var y = 0 ; y < g.list.length ; ++y) {
						var i = y;
						if (g.list[i].limited) {
							continue;
						}
						if (this.can_be_built(g.list[i].name, this.global_reserved_resources, local_reserved_resources) !== true) {
							// if we're here, building is not capped, but we do not have enough resources. not enough resources. this means that we
							// we'll be able to build it soon. we can't use these resources right now, but we can build something
							// that needs OTHER resources. so let's add resources needed for this building to "reserved_resources"
							// and check next position.
							continue;
						}
						if (rv === null) {
							rv = g.list[i];
						}
					}
					this.update_reserved_resources(local_reserved_resources);
				}
//				console.log(this.global_reserved_resources);
				return rv;
			},
			build: function() {
				this.render();
				if (!this.do_auto_build) { return; }
				if (gamePage.worker === undefined || gamePage.isPaused === true) {
					return; // avoid building when game is paused or not working, since this leads to anomalies
				}
				if (gamePage.activeTabId !== "Bonfire") { return ; }
				var candidate = this.select_candidate();
				if (candidate === null) {
					return;
				}
				x = $("div.btn:not(.disabled)>div:contains('" + candidate.label + "')");
				if (x.length > 0) {
					x.click();
					candidate.autobuilt++;
					this.render_pending = true;
					gamePage.msg('AutoBuild: ' + candidate.label);
				}
			},
			groups: [],
			rebuild_groups: function() {
				this.rescan();
				this.groups = [];
				for (var x in this.buildings) {
					var g = this.buildings[x].group;
					this.buildings[x].needed = gamePage.bld.getPrices(x);
					this.buildings[x].count = gamePage.bld.get(x).val;
					var new_limited = this.is_limited(this.buildings[x]);
					if (new_limited != this.buildings[x].limited) {
						this.buildings[x].limited = new_limited;
						if (new_limited === true) {
							$("#jdabld" + this.buildings[x].name + "span").addClass("limited");
						} else {
							$("#jdabld" + this.buildings[x].name + "span").removeClass("limited");
						}
					}
					if (this.groups[g] === undefined) {
						this.groups[g] = { list: [], reserved_resources: {} };
					}
					this.groups[g].list.push(this.buildings[x]);
					var res = this.buildings[x].needed;
					for (var r in res) {
						if (this.groups[g].reserved_resources[r.name] === undefined) {
							this.groups[g].reserved_resources[r.name] = r.val;
						} else {
							if (this.groups[g].reserved_resources[r.name] < r.val) {
								this.groups[g].reserved_resources[r.name] = r.val;
							}
						}
					}
				}
			},
			render: function() {
				this.rebuild_groups();
				if (this.render_pending !== true) {
					return;
				}
				this.render_pending = false;
				var s = "";
				var i = this.groups.length;
				s += "<ul>";
				for (var x = i - 1; x >= 0 ; x--) {
					if (this.groups[x] === undefined || this.groups[x].length === 0) {
						continue;
					}
					s += "<li><ul> GROUP " + x + "&nbsp;" + this.render_group_up_down_buttons(x);

					for (b in this.groups[x].list) {
						var bb = this.groups[x].list[b];
						s += "<li><div style='inline-block;'> "+ this.render_building_group_up_down_buttons(bb.name) + "<span id='jdabld" +
						bb.name + "span' class='" + (bb.limited ? 'limited' : '') + "'>" +
						bb.label + " " + "(" + bb.autobuilt + "/" + bb.count + ")</div></div></li>";
					}
					s += "</ul></li>";
				}
				s += "</ul>";
				$("#jda_busy_kittens_build").html(s);
			},
			group_up: function(y) {
				if (y === NaN) { return; }
				if (this.groups[y] === undefined) { return; }
				// ok, there is such group, check if group y + 1 exists. if yes, swap them,
				// otherwise simply renumber group to y + 1
				var l = this.groups[y].list;
				for (var b in l) {
					l[b].group = y + 1;
				}
				if (this.groups[y + 1] !== undefined) {
					l = this.groups[y + 1].list;
					for (b in this.groups[y + 1].list) {
						l[b].group = y;
					}
				}
				this.render_pending = true;
				jda_busy_kittens.save.save();
			},
			group_down: function(y) {
				if (y === NaN) { return; }
				if (this.groups[y] === undefined) { return; }
				if (y <= 1) { return; } // can't move anything to/from group 0 automatically
				var l = this.groups[y].list;
				for (var b in l) {
					l[b].group = y - 1;
				}
				if (this.groups[y - 1] !== undefined) {
					l = this.groups[y - 1].list;
					for (b in this.groups[y - 1].list) {
						l[b].group = y;
					}
				}
				this.render_pending = true;
				jda_busy_kittens.save.save();
			},
			building_group_up: function(n) {
				if (this.buildings[n] === undefined) {
					return ;
				}
				this.buildings[n].group += 1;
				jda_busy_kittens.save.save();
				this.render_pending = true;
			},
			building_group_down: function(n) {
				if (this.buildings[n] === undefined) {
					return;
				}
				if (this.buildings[n].group > 0) {
					this.buildings[n].group -= 1;
				}
				jda_busy_kittens.save.save();
				this.render_pending = true;
			},
			is_group_limited: function(group) {
				for (x in group.list) {
					if (group.list[x].limited !== true) {
						return false;
					}
				}
				return true;
			},
			render_group_up_down_buttons: function(number) {
				var s = "";
				s += "<a class='jdalnk' href='#' onclick='jda_busy_kittens.build.group_up(" + number + ");'>+</a>&nbsp;";
				s += "<a class='jdalnk' href='#' onclick='jda_busy_kittens.build.group_down(" + number + ");'>-</a>&nbsp;";
				return s;
			},
			render_building_group_up_down_buttons: function (name) {
				var s = "";
				s += "<a class='jdalnk' href='#' onclick='jda_busy_kittens.build.building_group_up(\"" + name + "\");'>+</a>&nbsp;";
				s += "<a class='jdalnk' href='#' onclick='jda_busy_kittens.build.building_group_down(\"" + name + "\");'>-</a>&nbsp;";
				return s;
			},
			craft: function(name) {
				if (this.do_auto_craft === false) {
					return;
				}
				// if do_auto_build is false, structure construction is not requested. so reset global_reserved_resources to 0, so anything can be crafted.
				if (this.do_auto_build === false) {
					this.global_reserved_resources = { };
				}
				// this function scans available crafts and compares them with global_reserved_resources. Since global_reserved_resources
				// keeps maximum of resource required for all buildings (i.e. if we have: library 100 wood, workshop 400 minerals and 150 wood,
				// and smelter 500 minerals, values stored are wood 150 and minerals 500), we can craft everything over these limits. This will
				// do funny things for wood, since wood can be near maximum and making more from catnip will make more than we can store, but I'm
				// not willing to fix it ATM.
				// 'ere we go. first for each resource check how much we can 'sacrifice'
				var available = { };
				var r = gamePage.resPool.resources;
				for (var x in r) {
					var have = r[x].value;
					var need = this.global_reserved_resources[r[x].name];
					need = (need === undefined ? 0 : need);
					if (have > 0 && have > need) {
						available[r[x].name] = have - need;
					}
				}
				var crafts = gamePage.workshop.crafts;
				for (var c in crafts) {
					var toCraft = Infinity;
					var prices = crafts[c].prices;
					for (r in prices) {
						var p = available[prices[r].name];
						if (p !== undefined && p !== NaN && p > prices[r].val) {
							var tmp = Math.floor(p / prices[r].val);
							toCraft = (tmp < toCraft) ? tmp : toCraft;
						} else {
							break; // nothing to do here
						}
					}
					if (toCraft !== Infinity && toCraft > 0) {
						gamePage.craft(crafts[c].name, toCraft);
						for (r in prices) {
							available[prices[r].name] -= toCraft * prices[r].val;
						}
//						console.log("Crafted " + (toCraft * (1 + gamePage.getResCraftRatio({name:crafts[c].name}))) + " " + crafts[c].name);
					}
				}
			},
			do_auto_build: false,
			toggle_auto_build: function() {
				if (this.do_auto_build === false) {
					this.render_pending = true;
					this.render();
					$('#jd_busy_kittens_build_toggle').prop('value', 'TURN OFF AUTO-BUILD');
				} else {
					this.render_pending = true;
					this.render();
					$('#jd_busy_kittens_build_toggle').prop('value', 'TURN ON AUTO-BUILD');
				}
				this.do_auto_build = !this.do_auto_build;
			},
			do_auto_craft: false,
			toggle_auto_craft: function() {
				if (this.do_auto_craft === false) {
					$("#jda_busy_kittens_craft_toggle").prop("value", "STOP AUTO-CRAFT");
					this.do_auto_craft = true;
				} else {
					$("#jda_busy_kittens_craft_toggle").prop("value", "START AUTO-CRAFT");
					this.do_auto_craft = false;
				}
			},
			register_tick_handler: function() {
				gamePage.originalTick = gamePage.tick;
				gamePage.tick = function() {
					gamePage.originalTick();
					jda_busy_kittens.build.build();
					jda_busy_kittens.build.craft();
				};
			}
		},
		save: {
			clear: function () {
				LCstorage.removeItem("kittensgame.busyKittens");
			},
			save: function() {
				LCstorage["kittensgame.busyKittens"] = JSON.stringify(jda_busy_kittens.build.buildings);
			},
			copyObject: function(source, target) {
				for (var attrname in source) {
					if (typeof source[attrname] === "object") {
						if (typeof target[attrname] === "undefined") {
							target[attrname] = {};
						}
						this.copyObject(source[attrname], target[attrname]);
					} else {
						target[attrname] = source[attrname];
					}
				}
			},
			load: function() {
				if (LCstorage["kittensgame.busyKittens"]) {
					this.copyObject(JSON.parse(LCstorage["kittensgame.busyKittens"]), jda_busy_kittens.build.buildings);
				}
			}
		},
		ui: {
			css_list: {
				main: "https://autokittens.googlecode.com/svn/busykittens.css"
			},
			load_single_css: function(filename) {
				var fileref=document.createElement("link");
				fileref.setAttribute("rel", "stylesheet");
				fileref.setAttribute("type", "text/css");
				fileref.setAttribute("href", filename);
				document.getElementsByTagName("head")[0].appendChild(fileref);
			},
			load_csses: function() {
				for (var x in this.css_list) {
					this.load_single_css(this.css_list[x]);
				}
			},
			build: function() {
				$('#footerLinks').append(' | <a href="#" onclick="$(\'#autoDiv\').toggle();">BusyKittens</a>');

				$('#importDiv').after('<div id="autoDiv" class="jda_help" style="display:none;overflow-y:scroll;"><a href="#" onclick="$(\'#autoDiv\').hide();" style="position: absolute; top: 10px; right: 15px;">close</a><div id="autoDivCont" ><table id="autoDivTab"></table></div><div id="jda_busy_kittens_build"></div></div>');

				$('#autoDivCont').append(
					'<input type="button" value="+10" href="#" onclick="jda_busy_kittens.speed.faster(10);">' + 
					'<input type="button" value="+5" href="#" onclick="jda_busy_kittens.speed.faster(5);">' + 
					'<input type="button" value="+1" href="#" onclick="jda_busy_kittens.speed.faster(1);">' + 
					'<input  type="button" id="gamespeedplaystop" href="#" value="STOP" onclick="jda_busy_kittens.speed.toggle();">' +
					'<span>SPEED: <span id="gamespeedcurrentrate">5</span></span>' +
					'<input  type="button" href="#" value="-1" onclick="jda_busy_kittens.speed.slower(1);">' +
					'<input  type="button" href="#" value="-5" onclick="jda_busy_kittens.speed.slower(5);">' +
					'<input  type="button" href="#" value="-10" onclick="jda_busy_kittens.speed.slower(10);">' +
					'&nbsp;&nbsp;&nbsp;<input type="button" href="#" value="RESET" onclick="jda_busy_kittens.speed.reset();"><br><br>' +
					'<input id="jda_busy_kittens_craft_toggle" type="button" href="#" value="START AUTO-CRAFT" onclick="jda_busy_kittens.build.toggle_auto_craft();"><br><br>');
				$('#autoDivCont').append('<br><input id="jd_busy_kittens_build_toggle" type="button" value="TURN ON AUTO-BUILD" onclick="jda_busy_kittens.build.toggle_auto_build(); return false;">');

				this.load_csses();
			}
		}
	};
	jda_busy_kittens.ui.build();
	jda_busy_kittens.save.load();
	jda_busy_kittens.build.register_tick_handler();
}

if (window.jda_busy_kittens === undefined) {
	jda_busy_kittens_initialise();
}
